home *** CD-ROM | disk | FTP | other *** search
- # Source Generated with Decompyle++
- # File: in.pyc (Python 2.4)
-
- '''POP3 Classes
-
- This module contains classes used for interacting with POP3 clients or
- servers. The module currently includes:
-
- POP3
- This class is identical to poplib.POP3, but redirects debugging output
- to a specified file. Some poplib.POP3 debugging output is also
- suppressed.
-
- SEPOP3Server
- This class is a simple POP3 server that acts as a proxy between a POP3
- client and a real POP3 server. All RFC 1725 commands are implemented,
- but no others (e.g. PIPELINING, STLS) apart from CAPA. The mail on the
- server is retrieved from the real POP3 server, once per instantiation
- of the class. Mail classified as spam is blocked (but may be released
- later elsewhere), and other mail is made available to connecting
- clients.
- '''
- import os
- import re
- import sys
- import md5
- import copy
- import time
- import email
- import poplib
- import thread
- import socket
- import string
- import asynchat
- import traceback
- import threading
- import email.Header as email
-
- try:
- import cStringIO as StringIO
- except ImportError:
- import StringIO
-
- from spambayes import Dibbler
- from spambayes.storage import NO_TRAINING_FLAG
- from spambayes.message import insert_exception_header
- from spamexperts import Options
- verbose = Options.options[('globals', 'verbose')]
- Options.options[('globals', 'verbose')] = False
- from spambayes.scripts.sb_server import BayesProxy, POP3ProxyBase
- Options.options[('globals', 'verbose')] = verbose
- del verbose
- from spamexperts import message, ProxyClassifier
- from se_config import spamexpertsConfig as configuration
- from spamexperts.OptionsClass import BLOCKED, DELAYED, REMOVED
- from spamexperts.OptionsClass import IS_HAM, IS_SPAM, IS_UNSURE
-
- class SEPOP3Proxy(BayesProxy, ProxyClassifier.ProxyClassifier):
- """Proxies between an email client and a POP3 server, inserting
- judgement headers. It acts on the following POP3 commands:
-
- o STAT:
- o Adds the size of all the judgement headers to the maildrop
- size.
-
- o LIST:
- o With no message number: adds the size of an judgement header
- to the message size for each message in the scan listing.
- o With a message number: adds the size of an judgement header
- to the message size.
-
- o RETR:
- o Adds the judgement header based on the raw headers and body
- of the message.
-
- o TOP:
- o Adds the judgement header based on the raw headers and as
- much of the body as the TOP command retrieves. This can
- mean that the header might have a different value for
- different calls to TOP, or for calls to TOP vs. calls to
- RETR. I'm assuming that the email client will either not
- make multiple calls, or will cope with the headers being
- different.
-
- o USER:
- o Does no processing based on the USER command itself, but
- expires any old messages in the three caches.
-
- This class does not provide any access to messages that have
- been delayed or blocked by the blocking POP3 proxy.
- """
-
- def __init__(self, clientSocket, unused, serverName, serverPort, state, ssl = False):
- self.skip_logging = False
- self.serverName = serverName
- self.serverPort = serverPort
- ProxyClassifier.ProxyClassifier.__init__(self)
- BayesProxy.__init__(self, clientSocket, serverName, serverPort, ssl)
- self.state = state
- self.state.proxies.append(self)
- self.state.model_notifier.SetBeginUpdating()
- self.past_headers = False
- self.past_ok = False
- self.last_character = None
-
-
- def onResponse(self):
- if self.serverName in Options.options[('pop3proxy', 'ignore_uidl_for_servers')]:
- for unsupported in [
- 'UIDL']:
- unsupportedLine = '(?im)^%s[^\\n]*\\n' % (unsupported,)
- self.response = re.sub(unsupportedLine, '', self.response)
-
-
- BayesProxy.onResponse(self)
-
-
- def send(self, data):
- '''Logs the data to the log file.'''
- if self.skip_logging:
- if '\n' in data:
- if data == '\n':
- lines = ('',)
- else:
- lines = data.split('\n')
- for line in lines:
- if not line == '':
- pass
- is_newline = line == '\r'
- if not (self.past_ok):
- if is_newline or line.startswith('+OK'):
- self.past_ok = True
- elif self.past_ok and is_newline and self.last_character == '':
- self.past_headers = True
-
- if self.past_headers:
- if (line.strip() == '.' or is_newline) and self.last_character == '.':
- self.skip_logging = False
-
- if self.skip_logging:
- self.state.logFile.write('...CONTENTS...\r\n')
- else:
- self.state.logFile.write(time.asctime() + ':' + line)
- self.last_character = ''
-
- elif data != '\r':
- self.state.logFile.write('...CONTENTS...\r\n')
- self.last_character = data[-1]
-
- else:
- self.state.logFile.write(time.asctime() + ':' + data)
- self.state.logFile.flush()
-
- try:
- return POP3ProxyBase.send(self, data)
- except socket.error:
- self.close()
-
- return 0
-
-
- def recv(self, size):
- '''Logs the data to the log file.'''
-
- try:
- data = POP3ProxyBase.recv(self, size)
- except socket.error:
- self.close()
- return ''
-
- if data.lower().startswith('user'):
- log_data = 'USER XXXXXXXX\r\n'
- elif data.lower().startswith('pass'):
- log_data = 'PASS XXXXXXXX\r\n'
- elif not (self.skip_logging) or data.lower().startswith('retr') or data.lower().startswith('top'):
- self.skip_logging = True
- self.past_headers = False
- self.past_ok = False
- self.last_character = None
- log_data = data
- else:
- log_data = data
- self.state.logFile.write(time.asctime() + ':' + log_data)
- self.state.logFile.flush()
- return data
-
-
- def close(self):
- if not self.isClosed:
- self.isClosed = True
- self.state.activeSessions -= 1
- POP3ProxyBase.close(self)
- self.serverSocket.close()
- self.state.proxies.remove(self)
- self.state.model_notifier.SetEndUpdating()
-
-
-
- def _do_classification(self, msg):
-
- try:
- self.classification_done = self.classify_message(msg)
- except:
- stream = StringIO.StringIO()
- traceback.print_exc(None, stream)
- details = stream.getvalue()
- self.classification_done = Exception(details)
-
-
-
- def onRetr(self, command, args, response):
- if re.search('\\n\\r?\\n', response):
- terminatingDotPresent = response[-4:] == '\n.\r\n'
- if terminatingDotPresent:
- response = response[:-3]
-
- (ok, messageText) = response.split('\n', 1)
-
- try:
- msg = email.message_from_string(messageText, _class = message.SEHeaderMessage)
- msg.setId(self.state.getNewMessageName())
- self.classification_done = False
- thread.start_new_thread(self._do_classification, (msg,))
- score = None
- for already_sent, c in enumerate(ok + '\n' + msg.as_string()[:60]):
- self.send(c)
- if self.classification_done:
- if isinstance(self.classification_done, Exception):
- raise self.classification_done
-
- (score, classification, clues) = self.classification_done
- break
-
- time.sleep(1)
-
- if score is None:
- while True:
- if self.classification_done:
- (score, classification, clues) = self.classification_done
- break
-
- time.sleep(1)
-
- if Options.options[('globals', 'verbose')]:
- print >>sys.stderr, 'Classification: %s' % (classification,)
-
- if classification == IS_HAM:
- classification = Options.options[('Headers', 'header_ham_string')]
- elif classification == IS_SPAM:
- classification = Options.options[('Headers', 'header_spam_string')]
- elif classification == IS_UNSURE:
- classification == Options.options[('Headers', 'header_unsure_string')]
-
- if command == 'RETR' and command == 'TOP' and len(args) == 2:
- pass
- correct_command = args[1] == '99999999'
- if classification == Options.options[('Headers', 'header_ham_string')] and Options.options[('Storage', 'no_cache_bulk_ham')]:
- pass
- isSuppressedBulkHam = msg.get('precedence') in [
- 'bulk',
- 'list']
- size_limit = Options.options[('Storage', 'no_cache_large_messages')]
- if size_limit > 0:
- pass
- isTooBig = len(messageText) > size_limit
- if correct_command and Options.options[('Storage', 'cache_messages')] and not isSuppressedBulkHam and not isTooBig:
- msg.RememberClassification(classification)
- msg.addHeaders(prob = score, clues = clues)
- if classification == Options.options[('Headers', 'header_spam_string')]:
- self.state.numSpams += 1
- corpus = self.state.spamCorpus
- elif classification == Options.options[('Headers', 'header_unsure_string')]:
- corpus = self.state.unsureCorpus
- else:
- self.state.numHams += 1
- corpus = self.state.hamCorpus
- msg = corpus.makeMessage(msg.getId(), msg.as_string())
- corpus.addMessage(msg, observer_flags = NO_TRAINING_FLAG)
-
- headers = []
- for name, value in msg.items():
- header = '%s: %s' % (name, value)
- headers.append(re.sub('\\r?\\n', '\r\n', header))
-
- body = re.split('\\n\\r?\\n', messageText, 1)[1]
- messageText = '\r\n'.join(headers) + '\r\n\r\n' + body
- retval = ok + '\n' + messageText
- if terminatingDotPresent:
- retval += '.\r\n'
-
- retval = retval[already_sent + 1:]
- except:
- (messageText, details) = insert_exception_header(messageText)
- print >>sys.stderr, details
- retval = ok + '\n' + messageText
- if terminatingDotPresent:
- retval += '.\r\n'
-
-
- return retval
- else:
- self.skip_logging = False
- return response
-
-
- def onUser(self, unused, unused2, response):
- '''Spins off two separate threads that expires any old messages
- in the caches, but does not do any processing of the USER command
- itself.'''
- thread.start_new_thread(self.state.spamCorpus.removeExpiredMessages, ())
- thread.start_new_thread(self.state.hamCorpus.removeExpiredMessages, ())
- return response
-
-
-
- class POP3(poplib.POP3):
- '''Just like the parent class, but redirects the debugging output.'''
-
- def __init__(self, logfile, host, port = poplib.POP3_PORT):
- self.logfile = logfile
- self.descriptor = '%s:%s' % (host, port)
- poplib.POP3.__init__(self, host, port)
-
-
- def _putline(self, line):
- self.sock.sendall('%s%s' % (line, poplib.CRLF))
-
-
- def _putcmd(self, line):
- if self._debugging:
- if line.lower().startswith('user'):
- repr_line = 'USER XXXXXXXX'
- elif line.lower().startswith('pass'):
- repr_line = 'PASS XXXXXXXX'
- else:
- repr_line = repr(line)
- self.logfile.write('%s: %s CMD: %s\r\n' % (time.asctime(), self.descriptor, repr_line))
-
- self._putline(line)
-
-
- def _getline(self):
- line = self.file.readline()
- if self._debugging > 1:
- self.logfile.write('%s: %s GET: %s\r\n' % (time.asctime(), self.descriptor, repr(line)))
-
- if not line:
- raise poplib.error_proto('-ERR EOF')
-
- octets = len(line)
- if line[-2:] == poplib.CRLF:
- return (line[:-2], octets)
-
- if line[0] == poplib.CR:
- return (line[1:-1], octets)
-
- return (line[:-1], octets)
-
-
- def _getresp(self):
- (resp, o) = self._getline()
- c = resp[:1]
- if c != '+':
- raise poplib.error_proto(resp)
-
- return resp
-
-
- def stat(self):
- retval = self._shortcmd('STAT')
- rets = retval.split()
- numMessages = int(rets[1])
- sizeMessages = int(rets[2])
- return (numMessages, sizeMessages)
-
-
-
- class POP3_SSL(poplib.POP3_SSL):
- '''Just like the parent class, but redirects the debugging output.'''
-
- def __init__(self, logfile, host, port = poplib.POP3_SSL_PORT, keyfile = None, certfile = None):
- self.logfile = logfile
- self.descriptor = '%s:%s' % (host, port)
- poplib.POP3_SSL.__init__(self, host, port, keyfile, certfile)
-
-
- def _putcmd(self, line):
- if self._debugging:
- if line.lower().startswith('user'):
- repr_line = 'USER XXXXXXXX'
- elif line.lower().startswith('pass'):
- repr_line = 'PASS XXXXXXXX'
- else:
- repr_line = repr(line)
- self.logfile.write('%s: %s CMD: %s\r\n' % (time.asctime(), self.descriptor, repr_line))
-
- self._putline(line)
-
-
- def _getline(self):
- debug = self._debugging
- self._debugging = 0
- (line, octets) = poplib.POP3_SSL._getline(self)
- self._debugging = debug
- if self._debugging > 1:
- self.logfile.write('%s: %s GET: %s\r\n' % (time.asctime(), self.descriptor, repr(line)))
-
- return (line, octets)
-
-
- def _putline(self, line):
- debug = self._debugging
- self._debugging = 0
- poplib.POP3_SSL._putline(self, line)
- self._debugging = debug
-
-
- def _getresp(self):
- (resp, o) = self._getline()
- c = resp[:1]
- if c != '+':
- raise poplib.error_proto(resp)
-
- return resp
-
-
-
- class POPRetriever(object):
-
- class _dummy_msg(object):
- '''Dummy message class used to check if a message is in the
- message info database.'''
-
- def __init__(self, key):
-
- self.getDBKey = lambda : key
- self.stored_attributes = [
- 'c',
- 't',
- 'block_state',
- 'account',
- 'date_modified',
- 'internaldate',
- 'flags',
- 'folder_name',
- 'uid']
- for att in self.stored_attributes:
- setattr(self, att, None)
-
-
-
-
- def close(self):
-
- try:
- self.state.proxies.remove(self)
- except ValueError:
- print >>sys.stderr, 'Connection missing from proxies list.'
-
-
- try:
- self.state.open_remote_connections.remove(self.current_account)
- except ValueError:
- print >>sys.stderr, 'Connection missing from open connections list.'
-
-
-
- def _normalise_name(self, name):
- return [](_[1])
-
-
- def _normalise_uid(self, uid):
- norm_current_account = self._normalise_name(self.current_account)
- norm_uid = self._normalise_name(uid)
- return '%s_%s' % (norm_current_account, norm_uid.lower())
-
- terminated = False
-
- def retrieveMessages(self):
- '''Collect any new mail from the remote server.'''
- p = self.remote_server
-
- try:
- response = p.uidl()
- except poplib.error_proto:
- pass
-
-
- try:
- (response, msg_list, unused) = p.list()
- except poplib.error_proto:
- None if self.serverName not in Options.options[('pop3proxy', 'ignore_uidl_for_servers')] else []
- e = None if self.serverName not in Options.options[('pop3proxy', 'ignore_uidl_for_servers')] else []
- print >>sys.stderr, 'Cannot list messages:', str(e)
- return None
- except:
- None if self.serverName not in Options.options[('pop3proxy', 'ignore_uidl_for_servers')] else []
-
- for msg in msg_list:
- if self.terminated:
- break
-
- (msg_id, unused) = msg.split()
- uid = None
-
- try:
- response = p.uidl(msg_id)
- except poplib.error_proto:
- e = None
-
- (unused, unused, raw_uid) = response.split(' ', 3)
- uid = self._normalise_uid(raw_uid)
- msg = self._dummy_msg(uid)
- self.state.message_info_database.load_msg(msg)
- if msg.c:
- continue
-
-
- try:
- p.set_debuglevel(0)
- self.state.logFile.write('RETR MESSAGE\r\n')
- self.state.logFile.flush()
- (response, text_list, unused) = p.retr(msg_id)
- p.set_debuglevel(2)
- except poplib.error_proto:
- e = None
- print >>sys.stderr, 'Cannot retrieve message', str(e)
- continue
-
- messageText = '\n'.join(text_list)
- if uid is None:
- raw_uid = md5.md5(messageText).hexdigest()
- uid = self._normalise_uid(raw_uid)
- msg = self._dummy_msg(uid)
- self.state.message_info_database.load_msg(msg)
- if msg.c:
- continue
-
-
- if Options.options[('globals', 'verbose')]:
- print 'Downloading new message (id %s, uid %s)' % (msg_id, uid)
-
- msg_info = {
- 'id': msg_id,
- 'server_uid': raw_uid,
- 'internal_id': uid,
- 'length': len(messageText) }
- self.processing_queue.put((messageText, uid, msg_info, self.current_account))
-
-
- try:
- p.quit()
- except (poplib.error_proto, socket.error):
- e = None
- print >>sys.stderr, 'Error quitting:', str(e)
-
- self.remote_server = None
- if Options.options[('globals', 'verbose')]:
- print 'Retrieving messages ended.'
-
-
-
- def delete_messages(self, p):
- messages_to_delete = self.state.delete_messages[self.current_account]
- to_delete = copy.copy(messages_to_delete.items())
- deleted = []
- for uid, msginfo in to_delete:
- if not msginfo:
- deleted.append(uid)
- continue
-
- old = self.state.delayed_messages[self.current_account]
-
- try:
- del old[uid]
- except KeyError:
- pass
-
- self.state.delayed_messages[self.current_account] = old
- self.state.delayed_messages.store()
- msg = self._dummy_msg(uid)
- self.state.message_info_database.load_msg(msg)
- msg.block_state = REMOVED
- self.state.message_info_database.store_msg(msg)
- msg_id = self._find_message_ID(p, msginfo['id'], msginfo['server_uid'])
- if msg_id == -1:
- if Options.options[('globals', 'verbose')]:
- print >>sys.stderr, "Couldn't find message to delete", msginfo['server_uid']
-
- deleted.append(uid)
- continue
-
- if Options.options[('globals', 'verbose')]:
- print 'Deleting message from server (id %s uid %s)' % (msg_id, uid)
-
- p.dele(msg_id)
- deleted.append(uid)
-
- for uid in deleted:
- del messages_to_delete[uid]
-
- self.state.delete_messages[self.current_account] = messages_to_delete
- self.state.delete_messages.store()
-
-
- def _find_message_ID(self, p, msg_id, correct_uid):
- '''Find the message id that matches the given uid, which is
- hopefully the value in id.
- '''
- if msg_id >= 0:
-
- try:
- (unused, unused, uid) = p.uidl(msg_id).split(' ', 3)
- except poplib.error_proto:
- e = None
-
- try:
- (response, text_list, unused) = p.retr(msg_id)
- except poplib.error_proto:
- e = None
- uid = None
-
- messageText = '\n'.join(text_list)
- uid = md5.md5(messageText).hexdigest()
-
- if uid == correct_uid:
- return msg_id
-
-
-
- try:
- (response, msg_list, unused) = p.list()
- except poplib.error_proto:
- e = None
- print >>sys.stderr, 'Cannot list messages:', str(e)
- return -1
-
- for msg in msg_list:
- (msg_id, unused) = msg.split()
-
- try:
- response = p.uidl(msg_id)
- except poplib.error_proto:
- continue
-
- (unused, unused, uid) = response.split(' ', 3)
- if uid == correct_uid:
- return msg_id
- continue
-
- for msg in msg_list:
- (msg_id, unused) = msg.split()
-
- try:
- (response, text_list, unused) = p.retr(msg_id)
- except poplib.error_proto:
- e = None
- uid = None
-
- messageText = '\n'.join(text_list)
- uid = md5.md5(messageText).hexdigest()
- if uid == correct_uid:
- return msg_id
- continue
-
- return -1
-
-
-
- class bigger_producer(asynchat.simple_producer):
-
- def __init__(self, data, buffer_size = 8192):
- asynchat.simple_producer.__init__(self, data, buffer_size)
-
-
-
- class SEPOP3Server(Dibbler.BrighterAsyncChat, ProxyClassifier.ProxyClassifier, POPRetriever):
- """Minimal POP3 server. All messages are obtained by
- downloading them from the 'real' server.
- """
-
- def __init__(self, clientSocket, socketMap, serverName, serverPort, state, ssl = False):
- self.isClosed = False
- ProxyClassifier.ProxyClassifier.__init__(self)
- Dibbler.BrighterAsyncChat.__init__(self, map = socketMap)
- Dibbler.BrighterAsyncChat.set_socket(self, clientSocket, socketMap)
- POPRetriever.__init__(self)
- self.ac_out_buffer_size = 2 ** 15
- self.ssl = ssl
- if self.ssl:
- self.pop_class = POP3_SSL
- else:
- self.pop_class = POP3
- self.serverName = serverName
- self.serverPort = serverPort
- self.threads = []
- self.creation_cache = { }
- self.corpus = state.hamCorpus
- self.waiting_corpus = state.waitingCorpus
- self.unsure_corpus = state.unsureCorpus
- self.state = state
- self.mail_items = ()
- self.state.proxies.append(self)
- self.set_terminator('\r\n')
- self.handlers = {
- 'QUIT': self.onQuit,
- 'CAPA': self.onCapa,
- 'STAT': self.onStat,
- 'LIST': self.onList,
- 'UIDL': self.onUidl,
- 'RETR': self.onRetr,
- 'TOP': self.onTop,
- 'USER': self.onUser,
- 'PASS': self.onPass,
- 'APOP': self.onApop,
- 'NOOP': self.onNoop,
- 'DELE': self.onDele,
- 'RSET': self.onRset }
-
- try:
- self.remote_server = self.pop_class(self.state.logFile, self.serverName, self.serverPort)
- except Exception:
- e = None
- self.push('-ERR %s' % (e,))
- self.close_when_done()
-
- self.remote_server.set_debuglevel(2)
- self.push(self.remote_server.getwelcome() + '\r\n')
- self.request = ''
- self.deleted_this_session = []
-
-
- def push(self, data):
- self.producer_fifo.push(bigger_producer(data, self.ac_out_buffer_size))
- self.initiate_send()
-
-
- def collect_incoming_data(self, data):
- '''Asynchat override.'''
- self.request = self.request + data
-
-
- def found_terminator(self):
- '''Asynchat override.'''
-
- try:
- (command, args) = self.request.split(None, 1)
- except ValueError:
- command = self.request
- args = ''
-
- command = command.upper()
- handler = self.handlers.get(command, self.onUnknown)
- self.push(handler(command, args))
- self.request = ''
-
-
- def close(self):
- if not self.isClosed:
- Dibbler.BrighterAsyncChat.close(self)
- thread.start_new_thread(self._wait_for_threads, ())
-
- self.isClosed = True
-
-
- def _wait_for_threads(self):
- '''Let the state know that we are completely done once all the
- threads are done.'''
- if Options.options[('globals', 'verbose')]:
- print 'Waiting for retrieve threads to complete for',
- if hasattr(self, 'current_acount'):
- print current_account
- else:
- print 'unknown account'
-
- for retrieve_thread in self.threads:
- if retrieve_thread.isAlive():
- retrieve_thread.join()
- continue
-
- if Options.options[('globals', 'verbose')]:
- print 'All retrieve threads complete for',
- if hasattr(self, 'current_acount'):
- print current_account
- else:
- print 'unknown account'
-
- self.threads = []
- self.state.proxies.remove(self)
-
- try:
- self.state.open_remote_connections.remove(self.current_account)
- except (ValueError, AttributeError):
- pass
-
- del self.socket
-
-
- def close_when_done(self):
- '''Asynchat override.'''
- Dibbler.BrighterAsyncChat.close_when_done(self)
- if not self.terminated:
- self.expungeMessages()
-
-
-
- def onQuit(self, *unused):
- self.push('+OK Goodbye!\r\n')
- self.close_when_done()
- return ''
-
-
- def onCapa(self, *unused):
- '''POP3 CAPA command.'''
- return '\r\n'.join([
- '+OK Capability list follows',
- 'TOP',
- 'USER',
- 'UIDL',
- '.',
- ''])
-
-
- def onStat(self, *unused):
- '''POP3 STAT command.'''
- local_msgs = self._getSortedMessageList()
- maildrop_size = []([ msginfo['length'] for msginfo in local_msgs ])
- return '+OK %d %d\r\n' % (len(local_msgs), maildrop_size)
-
-
- def _getSortedMessageList(self):
- data = _[1]
- data.sort()
- return [ d[1] for d in data ]
-
-
- def _creation_time(self, msg_id):
- if msg_id in self.creation_cache:
- return self.creation_cache[msg_id]
-
- msg = self.state.hamCorpus.get(msg_id)
- if msg is None:
- msg = self.state.waitingCorpus.get(msg_id)
-
- if msg is None:
- msg = self.state.unsureCorpus.get(msg_id)
-
- if msg is None:
- print >>sys.stderr, "Couldn't find message", msg_id
- return time.time()
-
- stat = os.stat(msg.pathname())
- creation_time = stat[9]
- self.creation_cache[msg_id] = creation_time
- return creation_time
-
-
- def _getList(self, args, msginfo_att):
- '''Implements the POP3 LIST and UIDL commands.'''
- if Options.options[('globals', 'verbose')]:
- start = time.time()
-
- if args:
-
- try:
- number = int(args)
- except ValueError:
- number = -1
-
- if number < number:
- pass
- elif number <= len(self.delayed_db):
- msginfo = self._getSortedMessageList()[number - 1]
- return '+OK %s\r\n' % msginfo[msginfo_att]
-
- return '-ERR no such message\r\n'
-
- return_lines = [
- '+OK']
- for i, msginfo in enumerate(self._getSortedMessageList()):
- size = msginfo[msginfo_att]
- return_lines.append('%d %s' % (i + 1, size))
-
- return_lines.append('.')
- if Options.options[('globals', 'verbose')]:
- print 'Getting list took', time.time() - start, 'seconds'
-
- return '\r\n'.join(return_lines) + '\r\n'
-
-
- def onList(self, unused, args):
- '''POP3 LIST command, with optional message number argument.'''
- return self._getList(args, 'length')
-
-
- def onUidl(self, unused, args):
- '''POP3 UIDL command.'''
- return self._getList(args, 'internal_id')
-
-
- def _getMessage(self, number, maxLines):
- '''Implements the POP3 RETR and TOP commands.'''
- if number < number:
- pass
- elif number <= len(self.delayed_db):
- msginfo = self._getSortedMessageList()[number - 1]
- msg = self.corpus.get(msginfo['internal_id'])
- if msg is None:
- msg = self.waiting_corpus.get(msginfo['internal_id'])
- if msg is None:
- msg = self.unsure_corpus[msginfo['internal_id']]
-
-
- msg.load()
- messageText = msg.as_string()
-
- try:
- (headers, body) = re.split('\\n\\r?\\n', messageText, 1)
- except ValueError:
- return '+OK %d octets\r\n%s\r\n.\r\n' % (len(messageText), messageText)
-
- if maxLines is None:
- body_lines = body.split('\n')
- else:
- body_lines = body.split('\n')[:maxLines]
- messageText = headers + '\r\n\r\n' + '\n'.join(body_lines)
- return '+OK\r\n%s\r\n.\r\n' % messageText
-
- return '-ERR no such message\r\n'
-
-
- def onRetr(self, unused, args):
- '''POP3 RETR command.'''
-
- try:
- number = int(args)
- except ValueError:
- number = -1
-
- return self._getMessage(number, None)
-
-
- def onTop(self, unused, args):
- '''POP3 RETR command.'''
-
- try:
- (number, lines) = map(int, args.split())
- except ValueError:
- (number, lines) = (-1, -1)
-
- return self._getMessage(number, lines)
-
-
- def _setupAccount(self):
- """Set the 'current_account' attribute, and create a new
- account for this user/server combination if necessary."""
- self.current_account = '%s_%s_POP' % (self.userName, self.serverName)
- if self.current_account not in self.state.blocked_messages:
- self.state.blocked_messages[self.current_account] = { }
- self.state.blocked_messages.store()
-
- if self.current_account not in self.state.delayed_messages:
- self.state.delayed_messages[self.current_account] = { }
- self.state.delayed_messages.store()
- m_f_s = email.message_from_string
- welcomeText = self.get_blocking_welcome_message(self.userName, self.serverName)
- msg = m_f_s(welcomeText, _class = message.SEHeaderMessage)
- msg.setId(self.state.getNewMessageName())
- corpus_msg = self.state.waitingCorpus.makeMessage(msg.getId(), msg.as_string())
- self.state.waitingCorpus.addMessage(corpus_msg, observer_flags = NO_TRAINING_FLAG)
- msg_id = msg.getId()
- msg_info = {
- 'server_uid': -1,
- 'id': -1,
- 'internal_id': msg_id,
- 'length': len(welcomeText) }
- self.state.delayed_messages.addMessage(self.current_account, msg_id, msg_info)
-
- if self.current_account not in self.state.delete_messages:
- self.state.delete_messages[self.current_account] = { }
- self.state.delete_messages.store()
-
- self.mail_items = tuple(self.delayed_db.items())
-
-
- def onUser(self, unused, args):
- '''POP3 USER command.'''
- if self.remote_server is None:
- return '-ERR Already authenticated'
-
-
- try:
- response = self.remote_server.user(args)
- except (poplib.error_proto, socket.sslerror):
- e = None
- return str(e) + '\r\n'
-
- self.userName = args
- self._setupAccount()
- return response + '\r\n'
-
-
- def onPass(self, unused, args):
- '''POP3 PASS command.'''
- if self.current_account in self.state.open_remote_connections:
- if Options.options[('globals', 'verbose')]:
- print 'Connection already open to', self.current_account, '- refusing new connection.'
-
- self.state.open_remote_connections.append(self.current_account)
- self.push('-ERR Mailbox is locked by another process. Another mail client is using this mailbox. Please try again in a few minutes.\r\n')
- self.close_when_done()
- return ''
-
- self.state.open_remote_connections.append(self.current_account)
- if self.remote_server is None:
- self.push('-ERR Already authenticated')
- self.close_when_done()
- return ''
-
-
- try:
- response = self.remote_server.pass_(args)
- except poplib.error_proto:
- e = None
- self.push(str(e) + '\r\n')
- self.close_when_done()
- return ''
-
- self.password = args
- connection = (self.serverName, self.serverPort, self.userName, self.password, 'pop3', self.ssl)
- self.state.model_notifier.add_connection(connection)
- self.use_apop = False
- retrieve_thread = threading.Thread(target = self.retrieveMessages)
- retrieve_thread.setDaemon(True)
- self.threads.append(retrieve_thread)
- retrieve_thread.start()
- return response + '\r\n'
-
-
- def onApop(self, unused, args):
- '''POP3 APOP command.'''
- if self.remote_server is None:
- return '-ERR Already authenticated'
-
-
- try:
- response = self.remote_server.apop(*args)
- except poplib.error_proto:
- e = None
- return str(e) + '\r\n'
-
- (self.userName, self.password) = args.split()
- self.use_apop = True
- self._setupAccount()
- retrieve_thread = threading.Thread(target = self.retrieveMessages)
- retrieve_thread.setDaemon(True)
- self.threads.append(retrieve_thread)
- retrieve_thread.start()
- return response + '\r\n'
-
-
- def onNoop(self, *unused):
- '''POP3 NOOP command.'''
- return '+OK NOOP successfull\r\n'
-
-
- def onDele(self, unused, args):
- '''POP3 DELE command.'''
-
- try:
- number = int(args)
- except ValueError:
- number = -1
-
- if number < number:
- pass
- elif number <= len(self.delayed_db):
- msginfo = self._getSortedMessageList()[number - 1]
- key = msginfo['internal_id']
- old = self.messages_to_delete
- old[key] = msginfo
- self.messages_to_delete = old
- self.deleted_this_session.append(key)
- return '+OK message deleted\r\n'
-
- return '-ERR no such message\r\n'
-
-
- def onRset(self, *unused):
- '''POP3 RSET command.'''
- for key in self.deleted_this_session:
- old = self.messages_to_delete
- del old[key]
- self.messages_to_delete = old
-
- self.deleted_this_session = []
- return '+OK\r\n'
-
-
- def onUnknown(self, command, unused):
- '''Unknown POP3 command.'''
- return '-ERR Unknown command: %s\r\n' % repr(command)
-
-
- def expungeMessages(self):
- '''Delete mail that the user has asked to be deleted.'''
-
- try:
- p = self.pop_class(self.state.logFile, self.serverName, self.serverPort)
- except Exception:
- e = None
- print >>sys.stderr, "Can't connect to delete:", str(e)
- return None
-
- if Options.options[('globals', 'verbose')]:
- p.set_debuglevel(2)
-
- if not hasattr(self, 'use_apop'):
- return None
-
- if self.use_apop:
- p.apop(self.userName, self.password)
- else:
-
- try:
- p.user(self.userName)
- except poplib.error_proto:
- e = None
- print >>sys.stderr, "Can't authenticate:", str(e)
- return None
-
-
- try:
- p.pass_(self.password)
- except poplib.error_proto:
- e = None
- print >>sys.stderr, "Can't authenticate:", str(e)
- return None
-
- self.delete_messages(p)
-
- try:
- p.quit()
- except poplib.error_proto:
- e = None
- print >>sys.stderr, 'Error quitting:', str(e)
-
-
-
-